﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using SimpleTaskManager.Domain.Entities;

namespace SimpleTaskManager.Domain.Concrete
{
    /// <summary>
    /// Represents the structure of the database
    /// </summary>
    public class EFDbContext : DbContext
    {
        #region Db sets
        /// <summary>
        /// Gets or sets the collection of users.
        /// </summary>
        public DbSet<User> Users { get; set; }

        /// <summary>
        /// Gets or sets the collection of tasks.
        /// </summary>
        public DbSet<Task> Tasks { get; set; }

        /// <summary>
        /// Gets or sets the collection of projects.
        /// </summary>
        public DbSet<Project> Projects { get; set; }

        /// <summary>
        /// Gets or sets the collection of skills.
        /// </summary>
        public DbSet<Skill> Skills { get; set; }

        /// <summary>
        /// Gets or sets the collection of user-skill associations.
        /// </summary>
        public DbSet<User_Skill> User_Skills { get; set; }

        /// <summary>
        /// Gets or sets the collection of the entries of worked hours.
        /// </summary>
        public DbSet<WorkingHoursEntry> WorkingHoursEntries { get; set; }

        /// <summary>
        /// Gets or sets the collection of the reports' meta-data.
        /// </summary>
        public DbSet<ReportHeader> ReportHeaders { get; set; }

        /// <summary>
        /// Gets or sets the collection of the reports' binary data.
        /// </summary>
        public DbSet<ReportDetail> ReportDetails { get; set; }
        #endregion

        /// <summary>
        /// This method is called when the model for a context has been initialized, but before the model has been locked down and used to initialize the context.
        /// </summary>
        /// <param name="modelBuilder">The builder that defines the model for the context being created.</param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Sets up the many-to-many relationship between Tasks and Users
            modelBuilder.Entity<Task>().
              HasMany(t => t.Group).    //Don't read literally, a task has many users, which is called a group
              WithMany(u => u.Tasks).
              Map(
               m =>
               {
                   m.MapLeftKey("TaskId");
                   m.MapRightKey("UserId");
                   m.ToTable("TaskUserGroup");
               });

            // Sets up the many-to-many relationship between Tasks and their requirements
            modelBuilder.Entity<Task>().
                HasMany(t => t.Requirements)
                .WithMany(u => u.RequiredBy)
                .Map(m =>
                    {
                        m.MapLeftKey("RequirerId");
                        m.MapRightKey("RequiredId");
                        m.ToTable("TaskRequirements");
                    });

            // Sets up the one-to-one relationship between the report headers and their details, 
            //  indicating that the details are optional
            modelBuilder.Entity<ReportHeader>()
                .HasOptional(h => h.Details)
                .WithRequired(d => d.Header);
        }

        /// <summary>
        /// Overrides the default behavior of validating only added and modified entities.
        /// </summary>
        /// <param name="entityEntry">DbEntityEntry instance that is supposed to be validated.</param>
        /// <returns>true to proceed with validation, otherwise false.</returns>
        protected override bool ShouldValidateEntity(System.Data.Entity.Infrastructure.DbEntityEntry entityEntry)
        {
            // Required to prevent bug - http://stackoverflow.com/questions/5737733
            if (entityEntry.Entity is ReportDetail)
            {
                return false;
            }
            return base.ShouldValidateEntity(entityEntry);
        }
    }
}
